package com.zx.zxojbackendjudge;

import cn.hutool.json.JSONUtil;

import com.zx.zxojbackendcommon.common.ErrorCode;
import com.zx.zxojbackendcommon.exception.BusinessException;
import com.zx.zxojbackendjudge.codesandbox.CodeSandBox;
import com.zx.zxojbackendjudge.codesandbox.CodeSandBoxFactory;
import com.zx.zxojbackendjudge.codesandbox.CodeSandBoxProxy;
import com.zx.zxojbackendjudge.strategy.JudgeContext;
import com.zx.zxojbackendmodel.dto.judge.ExecuteCodeRequest;
import com.zx.zxojbackendmodel.dto.judge.ExecuteCodeResponse;
import com.zx.zxojbackendmodel.dto.judge.JudgeInfo;
import com.zx.zxojbackendmodel.dto.question.JudgeCase;
import com.zx.zxojbackendmodel.dto.question.JudgeConfig;
import com.zx.zxojbackendmodel.entity.Question;
import com.zx.zxojbackendmodel.entity.QuestionSubmit;
import com.zx.zxojbackendmodel.enums.JudgeInfoMessageEnum;
import com.zx.zxojbackendmodel.enums.JudgeStatusEnum;

import com.zx.zxojbackendserviceclient.QuestionFeignClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class JudgeServiceImpl implements JudgeService{
    @Value("${codesandbox.type:example}")
    private String type;

    @Resource
    @Lazy
    private QuestionFeignClient questionFeignClient;
    @Resource
    private JudgeManager judgeManager;
    /**
     * 判题
     * @param questionSubmitId
     */
    public void doJudge(Long questionSubmitId) {
        //1.获取题目提交信息
        QuestionSubmit questionSubmit = questionFeignClient.getQuestionSubmitById(questionSubmitId);
        if(questionSubmit == null){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR,"提交不存在");
        }
        //2.判断提交状态，如果不为等待中就直接结束方法
        //乐观锁
        //** 我觉得在这个场景这个锁没用
        if(!questionSubmit.getStatus().equals(JudgeStatusEnum.WAITING.getValue())){
            return;
        }
        //3.查询题目信息
        Long questionId = questionSubmit.getQuestionId();
        Question question = questionFeignClient.getQuestionById(questionId);
        if(question == null){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR,"题目不存在");
        }
        //4.修改判题状态
        QuestionSubmit questionSubmitUpdate = new QuestionSubmit();
        questionSubmitUpdate.setId(questionSubmitId);
        questionSubmitUpdate.setStatus(JudgeStatusEnum.RUNNING.getValue());
        questionFeignClient.updateById(questionSubmitUpdate);
        //5.调用沙箱进行代码沙箱
        CodeSandBox codeSandBox = CodeSandBoxFactory.newInstance(type);
        CodeSandBoxProxy codeSandBoxProxy = new CodeSandBoxProxy(codeSandBox);
        //5.1获取语言，代码，输入示例
        String language = questionSubmit.getLanguage();
        String code = questionSubmit.getCode();
        String judgeCaseStr = question.getJudgeCase();
        List<JudgeCase> list = JSONUtil.toList(judgeCaseStr, JudgeCase.class);
        List<String> inputs = list.stream().map(JudgeCase::getInput).collect(Collectors.toList());
        //5.2构造ExecuteCodeRequest
        ExecuteCodeRequest executeCodeRequest = ExecuteCodeRequest.builder()
                .code(code)
                .language(language)
                .inputs(inputs)
                .build();
        //5.3调用沙箱
        ExecuteCodeResponse executeCodeResponse = codeSandBoxProxy.executeCode(executeCodeRequest);
        //6.选择判题策略进行判题
        JudgeConfig judgeConfig = JSONUtil.toBean(question.getJudgeConfig(), JudgeConfig.class);
        JudgeContext judgeContext = JudgeContext.builder()
                .executeCodeResponse(executeCodeResponse)
                .inputs(inputs)
                .judgeCase(list)
                .judgeConfig(judgeConfig)
                .language(language)
                .build();
        //6.1调用判题管理，帮我们根据语言选择对应的策略进行判题，返回判题结果
        JudgeInfo judgeInfo = judgeManager.doJudge(judgeContext);
        //7.更新数据库
        questionSubmitUpdate = new QuestionSubmit();
        questionSubmitUpdate.setStatus(JudgeStatusEnum.SUCCESS.getValue());
        String judgeInfoStr = JSONUtil.toJsonStr(judgeInfo);
        questionSubmitUpdate.setJudgeInfo(judgeInfoStr);
        questionSubmitUpdate.setId(questionSubmitId);
        if(judgeInfo != null){
            String message = judgeInfo.getMessage();
            if(!message.equals(JudgeInfoMessageEnum.ACCEPTED.getValue())){
                questionSubmitUpdate.setStatus(JudgeStatusEnum.FAILED.getValue());
            }
        }else {
            questionSubmitUpdate.setStatus(JudgeStatusEnum.FAILED.getValue());
        }
        questionFeignClient.updateById(questionSubmitUpdate);
    }
}
